#include "stdafx.h"

#include "gp_Vec2d.h"
#include "gp_XY.h"
#include "gp_Ax2d.h"
#include "gp_Trsf2d.h"
#include "gp_Dir2d.h"
#include "gp_XY.h"
#include "gp_Ax2d.h"
#include "gp_Trsf2d.h"
#include "gp.h"

 gp_Dir2d::gp_Dir2d() : coord (1.,0.) 
{
}

 gp_Dir2d::gp_Dir2d (const gp_Vec2d& V)
{
  const gp_XY& XY = V.XY();
  double X = XY.X();
  double Y = XY.Y();
  double D = sqrt(X * X + Y * Y);

  Standard_ConstructionError_Raise_if(D <= gp::Resolution(), "");
  coord.SetX(X / D);
  coord.SetY(Y / D);
}

 gp_Dir2d::gp_Dir2d (const gp_XY& XY)
{
  double X = XY.X();
  double Y = XY.Y();
  double D = sqrt(X * X + Y * Y);
  Standard_ConstructionError_Raise_if(D <= gp::Resolution(), "");
  coord.SetX(X / D);
  coord.SetY(Y / D);
}

 gp_Dir2d::gp_Dir2d (const double Xv,
			   const double Yv)
{
  double D = sqrt (Xv * Xv + Yv * Yv);
  Standard_ConstructionError_Raise_if(D <= gp::Resolution(), "");
  coord.SetX(Xv / D);
  coord.SetY(Yv / D);
}

 void gp_Dir2d::SetCoord (const int Index,
				const double Xi)
{
  double X = coord.X();
  double Y = coord.Y();
  Standard_OutOfRange_Raise_if(Index < 1 || Index > 2, " ");
  if      (Index == 1) X = Xi;
  else                 Y = Xi;
  double D = sqrt (X * X + Y * Y);
  Standard_ConstructionError_Raise_if(D <= gp::Resolution(), "");
  coord.SetX(X / D);
  coord.SetY(Y / D);
}

 void gp_Dir2d::SetCoord (const double Xv,
				const double Yv)
{
  double D = sqrt (Xv * Xv + Yv * Yv);
  Standard_ConstructionError_Raise_if(D <= gp::Resolution(), "");
  coord.SetX(Xv / D);
  coord.SetY(Yv / D);
}

 void gp_Dir2d::SetX (const double X)
{
  double Y = coord.Y();
  double D = sqrt (X * X + Y * Y);
  Standard_ConstructionError_Raise_if(D <= gp::Resolution(), "");
  coord.SetX(X / D);
  coord.SetY(Y / D);
}

 void gp_Dir2d::SetY (const double Y)
{
  double X = coord.X();
  double D = sqrt (X * X + Y * Y);
  Standard_ConstructionError_Raise_if(D <= gp::Resolution(), "");
  coord.SetX(X / D);
  coord.SetY(Y / D);
}

 void gp_Dir2d::SetXY (const gp_XY& XY)
{
  double X = XY.X();
  double Y = XY.Y();
  double D = sqrt(X * X + Y * Y);
  Standard_ConstructionError_Raise_if(D <= gp::Resolution(), "");
  coord.SetX(X / D);
  coord.SetY(Y / D);
}

 double gp_Dir2d::Coord (const int Index) const
{ return coord.Coord(Index); }

 void gp_Dir2d::Coord(double& Xv, double& Yv) const
{ coord.Coord (Xv, Yv); }

 double gp_Dir2d::X() const
{ return coord.X() ; }    

 double gp_Dir2d::Y() const
{ return coord.Y() ; }

 const gp_XY& gp_Dir2d::XY () const
{ return coord; }

 bool gp_Dir2d::IsEqual
(const gp_Dir2d& Other,
 const double AngularTolerance) const
{
  double Ang = Angle(Other);
  if (Ang < 0) Ang = - Ang;
  return   Ang <= AngularTolerance;
}    

 bool gp_Dir2d::IsNormal
(const gp_Dir2d& Other,
 const double AngularTolerance) const
{
  double Ang = Angle(Other);
  if (Ang < 0) Ang = - Ang;
  Ang = M_PI / 2.0 - Ang;
  if (Ang < 0) Ang = - Ang;
  return   Ang <= AngularTolerance;
}    

 bool gp_Dir2d::IsOpposite
(const gp_Dir2d& Other,
 const double AngularTolerance) const
{ 
  double Ang = Angle(Other);
  if (Ang < 0) Ang = - Ang;
  return M_PI - Ang <= AngularTolerance;
}    

 bool gp_Dir2d::IsParallel
(const gp_Dir2d& Other,
 const double AngularTolerance) const
{
  double Ang = Angle(Other);
  if (Ang < 0) Ang = - Ang;
  return   Ang <= AngularTolerance || M_PI - Ang <= AngularTolerance;
}    

 double gp_Dir2d::Crossed (const gp_Dir2d& Right) const
{ return coord.Crossed (Right.coord); }

 double gp_Dir2d::Dot (const gp_Dir2d& Other) const
{ return coord.Dot (Other.coord); }

 void gp_Dir2d::Reverse()
{ coord.Reverse(); }

 gp_Dir2d gp_Dir2d::Reversed() const
{ 
  gp_Dir2d V = *this;
  V.coord.Reverse ();
  return V;
}

 void gp_Dir2d::Rotate (const double Ang)
{
  gp_Trsf2d T;
  T.SetRotation (gp_Pnt2d (0.0, 0.0), Ang);
  coord.Multiply (T.HVectorialPart());
}

 gp_Dir2d gp_Dir2d::Rotated (const double Ang) const
{
  gp_Dir2d V = *this;
  V.Rotate (Ang);
  return V;
}

 gp_Dir2d gp_Dir2d::Transformed (const gp_Trsf2d& T) const
{
  gp_Dir2d V = *this;
  V.Transform (T);
  return V;
} 

 double gp_Dir2d::Angle (const gp_Dir2d& Other) const
 {
	 //    Commentaires :
	 //    Au dessus de 45 degres l'arccos donne la meilleur precision pour le
	 //    calcul de l'angle. Sinon il vaut mieux utiliser l'arcsin.
	 //    Les erreurs commises sont loin d'etre negligeables lorsque l'on est
	 //    proche de zero ou de 90 degres.
	 //    En 2D les valeurs angulaires sont comprises entre -PI et PI
	 double Cosinus = coord.Dot   (Other.coord);
	 double Sinus = coord.Crossed (Other.coord);
	 if (Cosinus > -0.70710678118655 && Cosinus < 0.70710678118655) { 
		 if (Sinus > 0.0) return   acos (Cosinus);
		 else             return - acos (Cosinus);
	 }
	 else {
		 if (Cosinus > 0.0)  return      asin (Sinus);
		 else { 
			 if (Sinus > 0.0) return  M_PI - asin (Sinus);
			 else             return -M_PI - asin (Sinus);
		 }
	 }
 }

 void gp_Dir2d::Mirror (const gp_Ax2d& A2)
 {
	 const gp_XY& XY = A2.Direction().XY();
	 double A = XY.X();
	 double B = XY.Y();
	 double X = coord.X();
	 double Y = coord.Y();
	 double M1 = 2.0 * A * B;
	 double XX = ((2.0 * A * A) - 1.0) * X + M1 * Y;
	 double YY = M1 * X + ((2.0 * B * B) - 1.0) * Y;
	 coord.SetCoord(XX,YY);
 }

 void gp_Dir2d::Transform (const gp_Trsf2d& T)
 {
	 if (T.Form() == gp_Identity || T.Form() == gp_Translation)    { }
	 else if (T.Form() == gp_PntMirror) { coord.Reverse(); }
	 else if (T.Form() == gp_Scale) {
		 if (T.ScaleFactor() < 0.0) { coord.Reverse(); }
	 }
	 else {
		 coord.Multiply (T.HVectorialPart());
		 double D = coord.Modulus();
		 coord.Divide(D);
		 if (T.ScaleFactor() < 0.0) { coord.Reverse(); }
	 } 
 }

 void gp_Dir2d::Mirror (const gp_Dir2d& V)
 {
	 const gp_XY& XY = V.coord;
	 double A = XY.X();
	 double B = XY.Y();
	 double X = coord.X();
	 double Y = coord.Y();
	 double M1 = 2.0 * A * B;
	 double XX = ((2.0 * A * A) - 1.0) * X + M1 * Y;
	 double YY = M1 * X + ((2.0 * B * B) - 1.0) * Y;
	 coord.SetCoord(XX,YY);
 }

 gp_Dir2d gp_Dir2d::Mirrored (const gp_Dir2d& V) const
 {
	 gp_Dir2d Vres = *this;
	 Vres.Mirror (V);
	 return Vres;
 }

 gp_Dir2d gp_Dir2d::Mirrored (const gp_Ax2d& A) const
 {
	 gp_Dir2d V = *this;
	 V.Mirror (A);
	 return V;
 }
